# -*- coding: utf-8 -*-

"""
Module implementing.
"""
import time

# 剔除多余的0x、h、和空格
def get_hexstr_pure(hexstr):
    # 从0xaa bb ccH ddh eeff字符串转换为 aabbccddeeff 这样子的形式
    ## 仅仅保留 0~f 的字符
    hexstr = hexstr.replace("H", "h")
    hexstr = hexstr.replace("h", "")
    hexstr = hexstr.replace("X", "x")
    hexstr = hexstr.replace("0x", "")
    hexstr = hexstr.replace(" ", "")
    # fromhex 要求输入的字节长度是偶数
    if len(hexstr) % 2 != 0:
        hexstr = "0" + hexstr
    return hexstr


# 把hex string转字节
def hex_string_to_bytes(hex_str):
    byte_value = bytes.fromhex(get_hexstr_pure(hex_str)) # 将十六进制字符串转换为字节类型
    return byte_value

# 把hex string 转int
def hex_string_to_int(hex_str):
    byte_value = hex_string_to_bytes(hex_str) # 将十六进制字符串转换为字节类型
    int_val = int().from_bytes(byte_value, byteorder='big', signed=False)
    return int_val

def split_string(s, n):
    result = []
    for i in range(0, len(s), n):
        result.append(s[i:i+n])
    return result

# 把hex string们转int列表
def hex_strings_to_int_list(hex_str):
    hex_str = get_hexstr_pure(hex_str)
    hex_list = split_string(hex_str, 2)
    result = list()
    for hexstr in hex_list:
        result.append(hex_string_to_int(hexstr))
    #print(result)
    return result

# 把hex string 补齐为32位
def hex_string_to_format_hex32(hex_str):
    # 限制长度在32以下，防止转换出错
    hex_str = get_hexstr_pure(hex_str)
    hex_str = hex_str[-8:]
    int_val = hex_string_to_int(hex_str)
    hex_str = ('{:08x}'.format(int_val))
    return hex_str


#python3.8.0 64位（python 32位要用32位的DLL）
#
from ctypes import *
VCI_USBCAN2 = 4
STATUS_OK = 1
class VCI_INIT_CONFIG(Structure):   
    _fields_ = [("AccCode", c_uint),
                ("AccMask", c_uint),
                ("Reserved", c_uint),
                ("Filter", c_ubyte),
                ("Timing0", c_ubyte),
                ("Timing1", c_ubyte),
                ("Mode", c_ubyte)
                ] 
class VCI_CAN_OBJ(Structure): 
    _fields_ = [("ID", c_uint),
                ("TimeStamp", c_uint),
                ("TimeFlag", c_ubyte),
                ("SendType", c_ubyte),
                ("RemoteFlag", c_ubyte),
                ("ExternFlag", c_ubyte),
                ("DataLen", c_ubyte),
                ("Data", c_ubyte*8),
                ("Reserved", c_ubyte*3)
                ] 

CanDLLName = './ControlCAN.dll' #把DLL放到对应的目录下
canDLL = windll.LoadLibrary('./ControlCAN.dll')

class ControlCAN(object):
    def __init__(self, dev_index = 0):
        self.DevIndex = dev_index
        self.reserveValue = 0
        self.canOpenFlag = False

    def to_can_bitrate(self, bitrate_str = "250k"):
        Timing0 = 0
        Timing1 = 0
        # 本适配器使用最新高速CAN收发器， 不再支持10K以下波特率
        #if bitrate_str == "10k" :
        #    Timing0=0x31;
        #    Timing1=0x1c;
        if bitrate_str == "20k" :
            Timing0=0x18;
            Timing1=0x1c;
        if bitrate_str == "40k" :
            Timing0=0x87;
            Timing1=0xff;
        if bitrate_str == "50k" :
            Timing0=0x09;
            Timing1=0x1c;
        if bitrate_str == "80k" :
            Timing0=0x83;
            Timing1=0xff;
        if bitrate_str == "100k" :
            Timing0=0x04;
            Timing1=0x1c;
        if bitrate_str == "125k" :
            Timing0=0x03;
            Timing1=0x1c;
        if bitrate_str == "200k" :
            Timing0=0x81;
            Timing1=0xfa;
        if bitrate_str == "250k" :
            Timing0=0x01;
            Timing1=0x1c;
        if bitrate_str == "400k" :
            Timing0=0x80;
            Timing1=0xfa;
        if bitrate_str == "500k" :
            Timing0=0x00;
            Timing1=0x1c;
        if bitrate_str == "666k" :
            Timing0=0x80;
            Timing1=0xb6;
        if bitrate_str == "800k" :
            Timing0=0x00;
            Timing1=0x16;
        if bitrate_str == "1000k" :
            Timing0=0x00;
            Timing1=0x14;
        if bitrate_str == "33k" :
            Timing0=0x09;
            Timing1=0x6f;
        if bitrate_str == "66k" :
            Timing0=0x04;
            Timing1=0x6f;
        if bitrate_str == "83k" :
            Timing0=0x03;
            Timing1=0x6f;
        return (Timing0, Timing1)

    def openCan(self, ch0_bit_rate_str = "250k", ch1_bit_rate_str = "250k"):
        ret = canDLL.VCI_OpenDevice(VCI_USBCAN2, self.DevIndex, self.reserveValue)

        if ret != STATUS_OK:
            print('调用 VCI_OpenDevice 出错')
            return False

        chn_bit_rate_str_list = [ch0_bit_rate_str , ch1_bit_rate_str]
        CANIndex_list = [0, 1]
        t0 = 0
        t1 = 0

        for i in range(0, 2): # 0, 1

            # AccCode  验收码。 SJA1000的帧过滤验收码。
            AccCode = 0x80000008
            # AccMask 屏蔽码。屏蔽码推荐设置为0xFFFFFFFF，即全部接收。
            AccMask = 0xFFFFFFFF
            # Filter 滤波方式，允许设置为0-3
            Filter = 0
            # can 速率配置
            t0, t1 = self.to_can_bitrate(chn_bit_rate_str_list[i])
            #print(t0, t1)
            # Mode 工作模式
            ## =0表示正常模式（相当于正常节点）
            ## =1表示只听模式（只接收，不影响总线）
            ## =2表示自发自收模式（环回模式） 。
            Mode = 0
            vci_initconfig = VCI_INIT_CONFIG(AccCode, AccMask, self.reserveValue, Filter, t0, t1, Mode)

            #初始0通道
            ret = canDLL.VCI_InitCAN(VCI_USBCAN2, self.DevIndex, CANIndex_list[i], byref(vci_initconfig))
            if ret != STATUS_OK:
                print('调用 VCI_InitCAN 出错')
                return False
            #print(CANIndex_list[i])
            ret = canDLL.VCI_StartCAN(VCI_USBCAN2, self.DevIndex, CANIndex_list[i])
            if ret != STATUS_OK:
                print('调用 VCI_StartCAN 出错')
                return False
        self.canOpenFlag = True
        print("Can OK")
        return True

    def closeCan(self):
        canDLL.VCI_CloseDevice(VCI_USBCAN2, self.DevIndex)
        self.canOpenFlag = False

    # 将帧ID限制在有效范围内
    ## 标准帧11位ID
    ## 扩展帧29位ID
    def toSafeFrameID(self, frame_id_hexstr, frame_format_str = "std") :

        # 帧ID 32位变量，数据格式为靠右对齐。 
        FrameId = hex_string_to_int(frame_id_hexstr)
        if frame_format_str == "std":
            ExternFlag = 0
            if FrameId > 0x7fff:
                FrameId = FrameId % 0x7fff
        else:
            ExternFlag = 1
            if FrameId > 0x1FFFFFFF:
                FrameId = FrameId % 0x1FFFFFFF
        return hex(FrameId)

    # 成功时返回True，失败时返回False
    def sendFrame(self, frame_id_hexstr, data_hexstr, chn_str = "ch0", frame_format_str = "std", frame_type_str = "data"):
        # 处理数据
        dataList = hex_strings_to_int_list(data_hexstr)

        ubyte_array = c_ubyte*8
        d1 = 0x00
        d2 = 0x00
        d3 = 0x00
        d4 = 0x00
        d5 = 0x00
        d6 = 0x00
        d7 = 0x00
        d8 = 0x00
        try:
            d1 = dataList[0]
            d2 = dataList[1]
            d3 = dataList[2]
            d4 = dataList[3]
            d5 = dataList[4]
            d6 = dataList[5]
            d7 = dataList[6]
            d8 = dataList[7]
        except Exception as e:
            pass

        ################## 填充帧结构体 ##################
        # 帧ID 32位变量，数据格式为靠右对齐。 
        FrameId = int(self.toSafeFrameID(frame_id_hexstr, frame_format_str), 16)

        # TimeFlag和TimeStamp只在此帧为接收帧时有意义
        TimeStamp = 0
        TimeFlag = 0
        # 发送帧类型
        ## =1时为单次发送（只发送一次， 发送失败不会自动重发，总线只产生一帧数据）
        ## =0时为正常发送（发送失败会自动重发，重发时间为4秒， 4秒内没有发出则取消）；
        SendType = 1
        
        # 是否是远程帧。 
        ## =0时为为数据帧
        ## =1时为远程帧（数据段空）。
        RemoteFlag = 1
        if frame_type_str == "data":
            RemoteFlag = 0
        else:
            RemoteFlag = 1

        #是否是扩展帧。 
        ## =0时为标准帧（11位ID）
        ## =1时为扩展帧（29位ID）
        ExternFlag = 1
        if frame_format_str == "std":
            ExternFlag = 0
        else:
            ExternFlag = 1

        # 数据长度 DLC (<=8)，
        ## 即CAN帧Data有几个字节。约束了后面Data[8]中的有效字节
        maxDataLen = 8
        DataLen = len(dataList)
        if DataLen >= maxDataLen:
            DataLen = maxDataLen
        #if RemoteFlag == 1:
        #    DataLen = 0

        # CAN帧的数据
        ## 由于CAN规定了最大是8个字节，所以这里预留了8个字节的空间，受DataLen约束。
        ## 如DataLen定义为3，即Data[0]、 Data[1]、 Data[2]是有效的。
        FrameData = ubyte_array(d1, d2, d3, d4, d5, d6, d7, d8)

        # 保留字节
        ubyte_3array = c_ubyte*3
        ReservedZone = ubyte_3array(0, 0 , 0)

        # frameId TimeStamp TimeFlag SendType RemoteFlag ExternFlag DataLen FrameData ReservedZone
        vci_can_obj = VCI_CAN_OBJ(FrameId, TimeStamp, TimeFlag, SendType, RemoteFlag, ExternFlag,  DataLen, FrameData, ReservedZone) #单次发送


        ################## 填充发送参数 ##################

        #设备索引，比如当只有一个USB-CAN适配器时，索引号为0，
        ## 这时再插入一个USB-CAN适配器那么后面插入的这个设备索引号就是1，以此类推。
        DevIndex = self.DevIndex

        # 通道选择
        ## 第几路 CAN。即对应卡的CAN通道号， CAN1为0， CAN2为1。
        CANIndex = 0
        if chn_str == "ch1":
            CANIndex = 1

        # 发送的帧数量
        ## 要发送的帧结构体数组的个数（发送的帧数量）。 最大为1000， 建议设为1，每次发送单帧，以提高发送效率
        FrameCnt = 1

        ret = canDLL.VCI_Transmit(VCI_USBCAN2, DevIndex, CANIndex, byref(vci_can_obj), FrameCnt)
        time.sleep(0.1)
        if ret != STATUS_OK:
            #print('CAN通道发送失败\r\n')
            return False
        return True


    # 成功时返回列表，失败时返回空列表
    def recvFrame(self, chn_str = "ch0", frame_cnt = 1, timeout_ms = 2000) :
        #设备索引，比如当只有一个USB-CAN适配器时，索引号为0，
        ## 这时再插入一个USB-CAN适配器那么后面插入的这个设备索引号就是1，以此类推。
        DevIndex = self.DevIndex

        # 通道选择
        ## 第几路 CAN。即对应卡的CAN通道号， CAN1为0， CAN2为1。
        CANIndex = 0
        if chn_str == "ch1":
            CANIndex = 1
        print(CANIndex)

        # WaitTime 保留参数。
        WaitTime = 0

        rx_buff_cnt = 1
        #vci_can_obj = VCI_CAN_OBJ_ARRAY(rx_buff_cnt)#结构体数组
        loop = 0
        max_loop = timeout_ms / 200
        
        current_frame_cnt = 0
        MaxFrameCnt = frame_cnt
        
        result = list()
        while True:
            raw_message = VCI_CAN_OBJ()
            time.sleep(0.2)
            ret = canDLL.VCI_Receive(VCI_USBCAN2, DevIndex, CANIndex, byref(raw_message), rx_buff_cnt, WaitTime)
            if ret > 0:#接收到ret个帧数据
                current_frame_cnt = current_frame_cnt + ret
                result.append(raw_message)

                #return raw_message
                #continue
                print('CAN接收成功')
                print('ID：')
                print(raw_message.ID)
                print('DataLen：')
                print(raw_message.DataLen)
                print('Data：')
                print(list(raw_message.Data))

            if current_frame_cnt >= MaxFrameCnt:
                return result


            loop = loop + 1
            if loop > max_loop:
                break
        return result

#Linux系统下使用下面语句，编译命令：python3 python3.8.0.py
#canDLL = cdll.LoadLibrary('./libcontrolcan.so')





##结构体数组类
#import ctypes
#class VCI_CAN_OBJ_ARRAY(Structure):
#    _fields_ = [('SIZE', ctypes.c_uint16), ('STRUCT_ARRAY', ctypes.POINTER(VCI_CAN_OBJ))]
#
#    def __init__(self,num_of_structs):
#                                                                 #这个括号不能少
#        self.STRUCT_ARRAY = ctypes.cast((VCI_CAN_OBJ * num_of_structs)(),ctypes.POINTER(VCI_CAN_OBJ))#结构体数组
#        self.SIZE = num_of_structs#结构体长度
#        self.ADDR = self.STRUCT_ARRAY[0]#结构体数组地址  byref()转c地址
#
#def recvFrame_broken(chn_str = "ch0", timeout_ms = 2000) :
#    #设备索引，比如当只有一个USB-CAN适配器时，索引号为0，
#    ## 这时再插入一个USB-CAN适配器那么后面插入的这个设备索引号就是1，以此类推。
#    DevIndex = self.DevIndex
#
#    # 通道选择
#    ## 第几路 CAN。即对应卡的CAN通道号， CAN1为0， CAN2为1。
#    CANIndex = 0
#    if chn_str == "ch1":
#        CANIndex = 1
#
#    # WaitTime 保留参数。
#    WaitTime = 0
#
#    rx_buff_cnt = 10
#    vci_can_obj = VCI_CAN_OBJ_ARRAY(rx_buff_cnt)#结构体数组
#    ret = canDLL.VCI_Receive(VCI_USBCAN2, DevIndex, CANIndex, byref(vci_can_obj.ADDR), rx_buff_cnt, WaitTime)
#    print(ret)
#    if ret > 0:
#        print('CAN通道收到\r\n')
#    loop = 0
#    max_loop = timeout_ms / 200
#    while ret <= 0:#如果没有接收到数据，一直循环查询接收。
#        ret = canDLL.VCI_Receive(VCI_USBCAN2, DevIndex, CANIndex, byref(vci_can_obj.ADDR), rx_buff_cnt, WaitTime)
#        if ret > 0:#接收到一帧数据
#            break
#        time.sleep(0.2)
#        #print('--CAN1通道接收\r\n')
#        loop = loop + 1
#        if loop > 5:
#            can0_recv_err = 1
#        break
#    if ret > 0:#接收到一帧数据
#        print('CAN2道接收成功\r\n')

def test_can():
    #myCan = ControlCAN.ControlCAN()
    myCan = ControlCAN()
    myCan.openCan("500k","500k")
    #time.sleep(2)
    
    #frame_id_hexstr, data_hexstr, chn_str = "ch0", frame_format_str = "std", frame_type_str = "data"):
    frameId = "0x34"
    myCan.sendFrame("0x112233445566", "1122334455667788")
    myCan.sendFrame("0x11", "11223344")
    
    
    result = myCan.recvFrame("ch1", 2, 4000)
    for can_obj in result:
        print('接收成功\r\n')
        print('ID：')
        print(can_obj.ID)
        print('DataLen：')
        print(can_obj.DataLen)
        print('Data：')
        print(list(can_obj.Data))
    myCan.closeCan()